// TestServer.cpp : Defines the entry point for the console application.
//

//#include "SSI_Socket.h"
//#include "SSI_CommandServer.h"
#include "SSI_SocketLibAPI.h"
#include "StandardLib.h"

#include <string>
#include <stdlib.h>
#include <iostream>

using namespace std;

#define  SSI_OPEN_REQUEST      131
#define  SSI_LINE_REQUEST      132
#define  SSI_RESPONSE_OUTPUT   133
#define  SSI_COMMAND           134
#define  SSI_BYE               135
#define  SSI_ACTION_REQUEST    136
#define  SSI_ACTION_RESPONSE   137

#define  SSI_ERROR_OUTPUT      140
#define  SSI_WARNING_OUTPUT    141
#define  SSI_FAILURE_OUTPUT    142

#define  SSI_FILE_REQUEST			 150			 
#define  SSI_FILE_RESPONSE     151
#define  SSI_FILE_ERROR        152

#define  SSI_RSP_PORT          200
#define  SSI_FLAG_REQUEST      210
#define  SSI_FLAG_RESPONSE     211


#define  SSI_VAR_RQST          300
#define  SSI_VAR_RSP           301

#define  SSI_DBG_RQST          500
#define  SSI_DBG_RSP           501
#define  SSI_DBG_STEP          510

#define  SSI_BPT_ADD           520
#define  SSI_BPT_RMV           521

#define  SSI_DBG_JMP           530
#define  SSI_DBG_OUT           531

#define  SSI_DBG_EVAL          540

#define  SSI_DBG_GOCTR         550
#define  SSI_DBG_TOLIN         551

#define  DEBUG_BIT             1

void KillConnection(SSI_SOCKET sd);

int DisplayResponse(CSockCmd& aCmd){
	if(!aCmd.Data) return 1;
	string s;
	s = aCmd.Data;
	if(s.length() > 0){
		cout << "Response: " << s.c_str() << endl;
	}
	return 1;
}

int main(int argc, char* argv[])
{
	  // Do we have enough command line arguments?
		
		//char sBuff[1024];
    int nPort = 4242;


    CCommandServer Srv;
    std_stream out;

		//string data;
		string pcHost = "localhost";

    if(argc > 1) {
		  pcHost = argv[1];
			cout << "Host: " << pcHost << endl;
    }

	  if(argc > 2){
	  	sscanf(argv[2], "%d", &nPort);
			printf("Port: %d\n", nPort);
  	}

    CSocketSSI::Init();
    if(!Srv.Init(String(pcHost.c_str()), nPort, out)){
      CSocketSSI::CleanUpSingle();
      return 0;
    }

    /*
		CSocketSSI::Init();

		////////////////////// INIT /////////////////
	  CSocketSSI aSock;
		SSI_ADDR RemoteAddr = aSock.LookupAddress(pcHost.c_str());
		if(RemoteAddr == INADDR_NONE){
			cout << "ERROR*** Could not resolve address: " << pcHost << endl;
			return 0;
		}

		SSI_SOCKET sd = aSock.Connect(RemoteAddr, nPort);
		if(sd == INVALID_SOCKET){
			cout << "ERROR*** Could not connect to " << pcHost << endl; 
			return 0;
		}

		aSock.myHandle = sd;

		char nm[1024];
		gethostname(nm, 1024);
		char* IP_addr = CSocketSSI::IPFromName(nm);
		cout << "Host Name is: " << nm << " [" << IP_addr << "]" << endl;

		CSockCmd aCmd;
		CSockCmd Ret;
		aCmd.Set(SSI_OPEN_REQUEST, 0, 0);

		cout << "Sending open request..." << endl;
		aCmd.SendMe(aSock);

		// This will be a new socket port
		aCmd.ReadMe(aSock);
		if(aCmd.ID == SSI_RSP_PORT){
			nPort = *(int*)(aCmd.Data);
			SSI_SOCKET newHandle = aSock.Connect(RemoteAddr, nPort);
			
			if(newHandle == INVALID_SOCKET){
				cout << "ERROR*** Could not connect to " << pcHost << endl; 
				aCmd.Set(RSP_NACK, 0, 0);
				aCmd.SendMe(aSock);
				return 0;
			}
			aCmd.Set(RSP_ACK, 0, 0);
			aCmd.SendMe(aSock);
			aSock.myHandle = newHandle;
		}

		cout << "New port accepted" << endl;

		if(!aCmd.ReadMe(aSock)){
			cout << "Could not read action request" << endl;
			return 0;
		}
    */


		char sBuff[4096];
		cout << "Enter either run file or <ENTER> for console (1 for default file): ";
		cin.getline(sBuff, 4096);
		string s = sBuff;
		if(s.length() > 0){
			if(strcmp(s.c_str(), "1") == 0){
				s = "c:\\test1.psc";
			}
      String script = s.c_str();
      cout << "Enter 1 for debug mode: ";
		  cin.getline(sBuff, 4096);
		  s = sBuff;
		  
      int flags = 0;
		  if(strcmp(s.c_str(), "1") == 0){
			  flags = 1;
		  }

      if(!Srv.runScript(script, flags, out)){
        out.Format("[SERVER] Error*** runScript failed");
        CSocketSSI::CleanUpSingle();
        return 0;
      }
    }else{
      if(!Srv.runConsole(out)){
        out.Format("[SERVER] Error*** runConsole failed");
        CSocketSSI::CleanUpSingle();
        return 0;
      }
    }

    if(!Srv.Loop(out)){
      out.Format("[SERVER] Error*** Loop failed");
      CSocketSSI::CleanUpSingle();
      return 0;
    }

      /*
			aCmd.Set(SSI_ACTION_RESPONSE, s.length(), s.c_str());
		}else{
			aCmd.Set(SSI_ACTION_RESPONSE, 0, 0);		
		}

		if(!aCmd.SendMe(aSock)){
			cout << "Could not send action response" << endl;
			return 0;
		}

		if(!aCmd.ReadMe(aSock)){
			cout << "Could not read action ACK response" << endl;
			return 0;
		}

		if(aCmd.ID != RSP_ACK){
			cout << "Action ACK response not received" << endl;
			return 0;
		}

		// Flag processing
		if(!aCmd.ReadMe(aSock)){
			cout << "Could not read flag request" << endl;
			return 0;
		}

		
		aCmd.Set(SSI_FLAG_RESPONSE, sizeof(flags), (char*)(&flags));

		if(!aCmd.SendMe(aSock)){
			cout << "Could not send flag response" << endl;
			return 0;
		}

		if(!aCmd.ReadMe(aSock)){
			cout << "Could not read flag ACK response" << endl;
			return 0;
		}

		if(aCmd.ID != RSP_ACK){
			cout << "Flag ACK response not received" << endl;
			return 0;
		}

		aCmd.SequenceCtr = 0;
		// Read only this first command
		if(!aCmd.ReadMe(aSock)) return 0;
		while(1){
			
			if(!aCmd.IsValidSequence()) break;
			if(aCmd.ID == SSI_LINE_REQUEST){
				sBuff[0] = 0;
				//cout << "Enter Command: ";
				cin.getline(sBuff, 4096);

				string s = sBuff;
				string s2;
				int ind = 0;

				if(strcmp(sBuff, "clear_ctr") == 0){
					cout << "Clearing counter" << endl;
					aCmd.SequenceCtr = 0;
					sBuff[0] = 0;
				}

				aCmd.Set(SSI_COMMAND, (int)strlen(sBuff), sBuff);
				if(!aCmd.SendMe(aSock)) break;

				if(!aCmd.ReadMe(aSock)) break;
				if(!aCmd.IsValidSequence()) break;
				if(aCmd.ID != RSP_ACK){
					if(aCmd.Size > 0){
						cout << "Error Message: " << aCmd.Data << endl;
					}
					aCmd.Set(SSI_BYE, 0, 0);
					aCmd.SendMe(aSock);
					break;
				}

				if(!aCmd.ReadMe(aSock)) break;
				if(!aCmd.IsValidSequence()) break;

			}else if(aCmd.ID == SSI_FILE_REQUEST){
				string sFile = aCmd.Data;
				FILE* fh = fopen(sFile.c_str(), "rb");
				char buff[4095];
				int iSize = 0;
				while((iSize = fread((void*)buff, 1, 4095, fh)) == 4095){
					aCmd.Set(SSI_FILE_RESPONSE, iSize, buff);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send file data" << endl;
						return 0;
					}

					if(!aCmd.ReadMe(aSock)) break;
					if(!aCmd.IsValidSequence()) break;
					if(aCmd.ID != RSP_ACK){
						if(aCmd.Size > 0){
							cout << "Error Message: " << aCmd.Data << endl;
						}
						aCmd.Set(SSI_BYE, 0, 0);
						aCmd.SendMe(aSock);
						break;
					}
				}
				fclose(fh);

				aCmd.Set(SSI_FILE_RESPONSE, iSize, buff);
				if(!aCmd.SendMe(aSock)){
					cout << "Could not send file data" << endl;
					return 0;
				}

				if(!aCmd.ReadMe(aSock)) break;
				if(!aCmd.IsValidSequence()) break;
				if(aCmd.ID != RSP_ACK){
					if(aCmd.Size > 0){
						cout << "Error Message: " << aCmd.Data << endl;
					}
					aCmd.Set(SSI_BYE, 0, 0);
					aCmd.SendMe(aSock);
					break;
				}


				if(iSize > 0){
					aCmd.Set(SSI_FILE_RESPONSE, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send end file data" << endl;
						return 0;
					}
				}
				if(!aCmd.ReadMe(aSock)) break;
				if(!aCmd.IsValidSequence()) break;
				if(aCmd.ID != RSP_ACK){
					if(aCmd.Size > 0){
						cout << "Error Message: " << aCmd.Data << endl;
					}
					aCmd.Set(SSI_BYE, 0, 0);
					aCmd.SendMe(aSock);
					break;
				}

				// Still need to read the next command, dummy
				if(!aCmd.ReadMe(aSock)) break;
				if(!aCmd.IsValidSequence()) break;

			}else if(aCmd.ID == SSI_DBG_RQST){
				cout << aCmd.Data << endl;

				char sBuff[4096];
				sBuff[0] = 0;
				cout << "Enter DEBUG Command: ";
				cin.getline(sBuff, 4096);
				
				string s = sBuff;
				string s2;
				int ind = 0;
				
				if(s == "j"){
					aCmd.Set(SSI_DBG_JMP, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send jump request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read jump response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
					
					aCmd.Set(RSP_ACK, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

				}else if(s == "o"){
					aCmd.Set(SSI_DBG_OUT, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send jump request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read jump response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
					
					aCmd.Set(RSP_ACK, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

				}else if((ind = s.find("v ")) >= 0){
					s2 = s.substr(ind+2, s.length());
					aCmd.Set(SSI_VAR_RQST, s2.length(), s2.c_str());
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send variable request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read variable response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != SSI_VAR_RSP){
						cout << "Invalid variable response" << endl;
						aCmd.Set(RSP_NACK, 0, 0);
						aCmd.SendMe(aSock);
						//CSocketSSI::CleanUp();
					}
					cout << " DEBUG: " << aCmd.Data << endl;
					aCmd.Set(RSP_ACK, 0, 0);

					if(!aCmd.SendMe(aSock)){
						cout << "Could not send variable ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
				}else if((ind = s.find("e ")) >= 0){
					s2 = s.substr(ind+2, s.length());
					aCmd.Set(SSI_DBG_EVAL, s2.length(), s2.c_str());
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send eval request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read eval response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != RSP_ACK){
						cout << "Invalid eval response" << endl;
						aCmd.Set(RSP_NACK, 0, 0);
						aCmd.SendMe(aSock);
						//CSocketSSI::CleanUp();
					}
					//cout << " DEBUG: " << aCmd.Data << endl;
					//aCmd.Set(RSP_ACK, 0, 0);

					if(!aCmd.SendMe(aSock)){
						cout << "Could not send eval ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
				}else if((ind = s.find("t ")) >= 0){
					s2 = s.substr(ind+2, s.length());
					aCmd.Set(SSI_DBG_TOLIN, s2.length(), s2.c_str());
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send to line request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read to line response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != RSP_ACK){
						cout << "Invalid to line response" << endl;
						aCmd.Set(RSP_NACK, 0, 0);
						aCmd.SendMe(aSock);
						//CSocketSSI::CleanUp();
					}

					if(!aCmd.SendMe(aSock)){
						cout << "Could not send to line ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
				}else if((ind = s.find("g ")) >= 0){
					s2 = s.substr(ind+2, s.length());
					aCmd.Set(SSI_DBG_GOCTR, s2.length(), s2.c_str());
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send go counter request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read go counter response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != RSP_ACK){
						cout << "Invalid go counter response" << endl;
						aCmd.Set(RSP_NACK, 0, 0);
						aCmd.SendMe(aSock);
						//CSocketSSI::CleanUp();
					}

					if(!aCmd.SendMe(aSock)){
						cout << "Could not send go counter ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
				}else if((ind = s.find("+b ")) >= 0){
					s2 = s.substr(ind+2, s.length());
					aCmd.Set(SSI_BPT_ADD, s2.length(), s2.c_str());
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send breakpoint request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read breakpoint response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != RSP_ACK){
						cout << "Breakpoint NACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					aCmd.Set(RSP_ACK, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}
				}else if((ind = s.find("-b ")) >= 0){
					s2 = s.substr(ind+2, s.length());
					aCmd.Set(SSI_BPT_RMV, s2.length(), s2.c_str());
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send breakpoint removal request" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read breakpoint removal response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != RSP_ACK){
						cout << "Breakpoint removal NACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					aCmd.Set(RSP_ACK, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

				}else{
					aCmd.Set(SSI_DBG_STEP, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send step command" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(!aCmd.ReadMe(aSock)){
						cout << "Could not read debug step response" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					if(aCmd.ID != RSP_ACK){
						cout << "Debug Step NACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					aCmd.Set(RSP_ACK, 0, 0);
					if(!aCmd.SendMe(aSock)){
						cout << "Could not send ACK" << endl;
						CSocketSSI::CleanUp();
						return 0;
					}

					// Step through
				}

				// Read the next command... this is in the major loop
				if(!aCmd.ReadMe(aSock)) return 0;
			}else	if(aCmd.ID == RSP_NACK){
				cout << "NACK Received" << endl;
				if(aCmd.Size > 0){
					cout << "  Message: " << aCmd.Data << endl;
				}

			}else if(aCmd.ID == SSI_RESPONSE_OUTPUT || aCmd.ID == SSI_ERROR_OUTPUT || aCmd.ID == SSI_WARNING_OUTPUT || aCmd.ID == SSI_FAILURE_OUTPUT){
				if(aCmd.Size > 0){
					cout << aCmd.Data;
				}
				aCmd.Set(RSP_ACK, 0, 0);
				if(!aCmd.SendMe(aSock)) break;
				if(!aCmd.ReadMe(aSock)) break;
				if(!aCmd.IsValidSequence()) break;
			}
		}
*/
/*
		if(aSock.SendCommand(sd, aCmd).ID == RSP_ERROR){
			KillConnection(sd);
			CSocketSSI::CleanUp();
			return 0;
		}


		Ret = aSock.ReadCommand(sd);
		if(Ret.ID == RSP_NACK){
				cout << "ERROR*** Could not log on..." << endl; 
				KillConnection(sd);
				CSocketSSI::CleanUp();
				return 0;
		}

		while(1){
			Ret = aSock.ReadCommand(sd);
			if(Ret.ID == SSI_LINE_REQUEST){
				aCmd.Set(RSP_ACK, 0, 0);
 				if(aSock.SendCommand(sd, aCmd).ID == RSP_ERROR){
					KillConnection(sd);
					CSocketSSI::CleanUp();
					return 0;
				}


				sBuff[0] = 0;
				cin.getline(sBuff, 4096);

				aCmd.Set(SSI_COMMAND, (int)strlen(sBuff), sBuff);

				if(aSock.SendCommand(sd, aCmd).ID == RSP_ERROR){
					KillConnection(sd);
					CSocketSSI::CleanUp();
					return 0;
				}


				Ret = aSock.ReadCommand(sd);
				if(Ret.ID == RSP_NACK){
						cout << "ERROR*** Client refused command." << endl; 
						KillConnection(sd);
						break;
				}
			}else if(Ret.ID == SSI_RESPONSE_OUTPUT){
				printf("%s", Ret.Data);
				aCmd.Set(RSP_ACK, 0, 0);
 				if(aSock.SendCommand(sd, aCmd).ID == RSP_ERROR){
					KillConnection(sd);
					CSocketSSI::CleanUp();
					return 0;
				}

			}else if(Ret.ID == SSI_BYE){
				cout << "Connection killed by client" << endl; 
				KillConnection(sd);
				break;
			}else{
				cout << "ERROR*** Unknown Request or NACK" << endl; 
				KillConnection(sd);
				break;
			}
		}
		*/
    // Shut Winsock back down and take off.
		//CSocketSSI::CleanUp();
    //return retval;
}

/*
void KillConnection(SSI_SOCKET sd){
	CSocketSSI aSock;
	CSockCmd aCmd;
	printf("Killing Connection...\n");
	aCmd.Set(SSI_BYE, 0, 0);
	aSock.SendCommand(sd, aCmd);
	aSock.Disconnect(sd);
}
*/
